December 92 - APPLE EVENT CODING THROUGH OBJECTS
APPLE EVENT CODING THROUGH OBJECTS
ERIC M. BERDAHL
In "Apple Event Objects and You" in develop Issue 10, Richard Clark discusses a
procedural approach to programming for Apple events and goes into details of the Apple
event object model. This article reveals a few simple truths about the significance of
Apple events and the Apple event object model, focusing on how the object model maps
onto a typical object-oriented application. It also provides an object- oriented C++
framework for adding scripting support.
It's every developer's worst nightmare: Your team has just spent the last two years
putting the finishing touches on the latest version of Turbo WhizzyWorks II NT Pro,
which does everything, including make coffee. As a reward for your great work, the
team is now preparing to do some serious tanning development on an exotic island.
Then, Marketing comes in with "one last request." They promise it's the last thing
they'll ask for before shipping, and in a weak moment, you agree that one last little
feature won't hurt your itinerary. "Good," quips the product manager, "then as soon as
you add full scripting support, you can enjoy your vacation.
You know that to add scripting support, you need to delve into Apple events. You think
this requires learning about Apple events, the Apple event object model, and scripting
systems. Further, you think Apple events must be designed into your application from
the ground up and can't possibly be added without a complete redesign. Which of the
following is the appropriate reaction to Marketing's request?
A. Immediately strangle your sales manager and plead justifiable homicide.
B. Look around while laughing hysterically and try to find the hidden Candid Camera.
C. Change jobs.
D. Feign deafness.
E. None of the above.
Unfortunately, there's no correct answer, but the scenario is all too real as developers
are increasingly being asked to add scripting support to their applications. The design
of Apple events and the Apple event object model can provide the user with more power
than any other scripting system. However, to access the power of the design you need to
work with the complex interface provided by the Apple Event Manager. By its nature,
this interface collapses to a procedural plane of programming that prevents developers
from fully taking advantage of the object-oriented design inherent in the Appleevent
world. The Apple event object model is difficult to implement without some fancy
footwork on the part of your framework. But remember the words of Marshall
Brodeen, "All magic tricks are easy, once you know the secret." With this in mind,
join me on a trip through the rabbit hole into AppleEventLand.
WHAT ARE APPLE EVENTS AND THE OBJECT MODEL?
Whenever I give presentations on Apple events, the audience has an overwhelming urge
to ignore the theory and jump into coding. Resist the urge. For most developers Apple
events provide an unfamiliar perspective on application design. To appreciate the
significance of Apple events and the object model, it's important to understand their
underlying concepts and background. So, although you'll be reading about code later, a
little theory needs to come first.
At the most basic level, Apple events are a program-to-program communication
(PPC) system, whereprogram is defined as a piece of code that the Macintosh can see
as an application (in other words, that has a real WaitNextEvent-based event loop).
However, billing Apple events as PPC is akin to describing an F-16 as merely a plane.
To fully understand how Apple events are more than simple program-to-program
communication, you need to take a look at the Apple event object model.
The object model isn't really defined in a pithy paragraph ofInside Macintosh , but is
instead a holistic approach to dealing with things that users call objects. In a literal
sense, the object model is a software developer's description of user-centric objects
orcognitive objects.
COGNITIVE THEORY
Cognitive science tells us that people interact with the world through objects. A
printed copy ofdevelop is an object, a plant in the corner of your office is an object,
and a can of Coke Classic on your desk is an object. Each of the objects has properties,
behaviors, and parts. Some properties exist for each of the objects (for example, each
one has aname ) and other properties make sense for only some of the objects (for
example,page size makes sense only when applied todevelop ). Behaviors are quite
similar to properties in their ephemeral binding to objects. Only Coke willfizz , but
all three objects will decompose . However, they eachdecompose in a different way.
Further, each object can be separated into arbitrary parts that are themselves
objects. The plant can be separated into branches, which can in turn be separated into
leaves. The plant itself can also be separated into leaves, so leaves are contained by
both branch objects and plant objects.
BACK INSIDE THE COMPUTER
Now, since a user will someday interact with your software, and since users interact
with the world in terms of cognitive objects, it makes sense to model software in
terms of cognitive objects. Hence, the object model describes objects in a rather
ghostlike fashion whereby objects have behaviors and properties and contain other
objects. Although the object model defines an inheritance for each category of objects
(for example, Journal might inherit from OpenableThing which might inherit from
Object), it's used only for the purpose of grouping similar behaviors. Just as in the
mind, the only thing that's important is the identity of a specific object in existence at
a given time -- its categorization is purely a detail of implementation.
Gee, this sounds a lot like whatreal programmers mean when they talk about objects.
Strangely enough, real objects and cognitive objects are quite related. Many references
cite cognitive theory as justification for beginning to program in an object-oriented
style. Object-oriented code tries to get closer to the language of the native operating
system of the human mind than traditional procedural approaches, and the format of an
Apple event object mirrors natural language to a surprisingly large degree. It comes
as no surprise, then, that Good Object Design lends itself quite easily to slipping in
support for Apple event scripting.
APPLE EVENT OBJECTS AND SCRIPTING
The motivation for you to provide object model support is so that your users can
"script" your application. There are a variety of solutions available today that allow
advanced users to write things that resemble DOS batch files or UNIX® shell scripts.
These entities are commonly calledscripts , but in the context of Apple events a script
is something with greater potential. Whenever a user thinks "Iwant to sharpen the
area around the rose in this picture," a script has been formed. If this seems too
simplistic, consider it again. Script here refers to the earliest conception of a user's intent to do something. It's not relegated to the world of the computer and does not
imply any given form or class of forms; an oral representation (voice interface a la
the Knowledge Navigator) is equally as valid as a written one (traditional scripting
systems). From this perspective, the definition ofscript takes the user to a greater
depth of control over applications than previously dreamed of, allowing access to the
very engine of your application by the very engine of the user. This is the great
empowering ability of Apple events: they enable users to use their native operating
system -- the mind -- with little or no translation into computerese.
OBJECT-ORIENTED PROGRAMMING OBJECTS
The biggest problem with Apple event objects is the interface provided by the Apple
Event Manager. Instead of allowing you to write real object-oriented source code
using a given class library that implements basic Apple event and object model
functionality, the Apple Event Manager requires you to register every detail
programmatically. You must declare what classes exist, which methods exist and
where, and what relationships are possible within and between classes. Although at
first this flexibility seems advantageous, many developers find it a problem later
when they have to declare everything again at run time. Anyone with secret desires to
design an object-oriented runtime environment and a compiler/linker combination to
support that environment will feel quite at home with Apple event coding.
The second biggest problem with Apple event objects is that programs aren't written
in the Apple event (user) world. Instead, they're often written in object-oriented
programming languages like LISP and C++. What's needed is a good generic interface to
translate objects from the user world of natural language into the world of LISP or
C++ objects. Scripting systems do some of the work by delivering Apple event objects
to applications in the form of object specifiers, a strange data structure that
resembles a binary form of natural language stuffed into the familiar Apple event
generic data structure AEDesc. However, object-oriented applications ship objects
around in the form of . . . well . . . objects! So, you need translation from binary
natural language to actual objects. Easy, huh? (Don't hurt me yet -- this will seem
fairly straightforward after reading a bit further.)
Presenting a new interface should solve the problem of the Apple Event Manager
interfaces. Presenting that new interface in terms of the familiar object-oriented
class libraries should solve the problem of different paradigms. So, if these two
problems are approached with an object perspective, it's clear that some of the classes
in your program need to include a set of methods that implement object model
protocols. Application domain classes must be able to return objects contained within
them and to perform generic operations on themselves. It turns out that if your classes
also provide the ability to count the number of a specific type of object they contain,
you can provide a rudimentary, yet powerful, parsing engine for transforming objects
from the Apple event world into the traditional object programming world.
Further analysis indicates that only those application domain classes that correspond
to object model classes need this protocol. This indicates that the protocol for
providing Apple event object model support is probably appropriate to provide in a
mixin class (a class that's meant to be multiply inherited from). In this way, only
those classes that need to provide object model support must provide the necessary
methods. In the sample application discussed later, that class is called MAppleObject.
MAppleObject plays a key role in UAppleObject, a generic unit that can be used to
provide Apple event object model support to any well-designed C++ application.
Apple provides a convenient solution to the user versus programming language
problem in the form of the Object Support Library (OSL). The OSL has the specific
responsibility of turning an object specifier into an application's internal
representation of an object. (See "A Sample OSL Resolution" for an example of how the
OSL actually works.) The OSL implements a generic parsing engine, applying a few
simple assumptions about the state of the application's design to the problem.
However, for all the power provided by the engine within the OSL, it lacks an
object-oriented interface. Instead, it uses a paradigm like that provided by the Apple
Event Manager, requiring the application to register a set of bottleneck routines to
provide application-specific functionality. As with the Apple Event Manager, you must
write routines that implement runtime dispatching to theindividual objects your
application creates instead of using the natural method-dispatching mechanisms found
in your favorite object-oriented language, whatever it may be.
A SAMPLE OSL RESOLUTION
Here's a short example to give you a feel for how the OSL actually works. Don't read too
much into the details of object resolution, but do try to understand the flow and
methodology the OSL applies to resolve object specifiers. Also, don't worry too much
about how the OSL asks questions; the protocol you'll actually be using in UAppleObject
hides such details from you.
Figure 1 on the next page gives an overview of the process. Consider the simple object
specifier "the third pixel in the first scan line of the image called 'Girl with Hat,'" and
an Apple event that says "Lighten the third pixel in the first scan line of the image
called 'Girl with Hat' by twenty gray levels." On receiving this Apple event (Lighten)
the application notes that the direct object of the event (the third pixel in the first
scan line of the image called "Girl with Hat") is an object specifier and asks the OSL to
resolve it into a real object.
At this point the parsing engine in the OSL takes over, beginning a dialog with your
application through a set of preregistered callback routines. Notice that the object
specifier bears a striking resemblance to a clause of natural language -- English in
this case. This is not unintentional. Apple event objects are cognitive objects, and
cognitive objects are described by natural language -- hence the parallels between
object specifier formats and natural language. Further, the parsing engine inside the
OSL operates like a high school sophomore parsing sentences at the chalkboard. But I
digress . . .
To continue, the OSL asks the null object to give it a token for the image called "Girl
with Hat." (Tokens are the Coin of the Realm to the OSL.) So the null object looks
through its images to find the one named "Girl with Hat" and returns a token to it.
The OSL then turns around and asks the image called "Girl with Hat" to give it a token
for the first scan line. After getting this token, the OSL has no further use for the
image token, so it's returned to the application for disposal. In effect, this says, "Uh,
hey guys, I'm done with this token. If you want to do anything like free memory or
something, you can do it now." Notice how polite the OSL is.
Next, the OSL asks the scan line for a token representing the third pixel, which the
line handily returns. Now it's the scan line token's turn to be returned to the
application for recycling. The OSL has no further use for the scan line token, so the
application can get rid of it if necessary.
Finally, having retrieved the token for the third pixel of the first line of the image
called "Girl with Hat," the OSL returns the token with a "Thanks, and come again." The
application can then ask the object represented by the token to lighten itself
(remember that was the original Apple event), and dispose of the token for the pixel.
As you can see, the OSL operates by taking an unreasonable request, "give me the third
pixel of the first line of the image called "Girl with Hat," and breaks it into a number
of perfectly reasonable requests. Thus, your application gets to take advantage of its
innate knowledge of its objects and their simple relationships to answer questions
about complex object relationships.
Figure 1 Resolving an Object Specifier
The nicest thing about the OSL is that, like the Apple Event Manager itself, it applies
itself quite well to being wrapped with a real object-oriented interface (although you
have to write it yourself, sigh). Curiously, the OSL solves both problems -- poor